import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;



public class jcvfpdata {
	
	static String 	file_Import_Name;
	static String 	file_Export_TabName;
	static String 	file_Export_OutName;
	//
	static String	CLONE 				= "_clone.csv";
	static String	TEMP 				= "_clone.csv_temp.csv";
	//
	static boolean 	exportOut 			= false;
	//
	private	int 	allcount 			= 0;
	private	int 	memberCount 		= 0;
	private boolean first 				= true;
	private boolean finish				= false;
	private List<List<String>> stringList;
	//
	public static final int SUBLIST_USERID = 0;
	public static final int SUBLIST_BOOKID = 1;
	public static int ARRAY_USERID = -1;
	public static int ARRAY_BOOKID = -1;

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		jcvfpdata jcv = new jcvfpdata();
		
		String tempArgs[] = new String[args.length];
		
		for (int i = 0; i < args.length; i++) {
			tempArgs[i] = args[i].toLowerCase();
		}
		
		if (tempArgs.length >= 6 && tempArgs[0].compareTo("-f") == 0 && new File(args[1]).exists() == true && tempArgs[1].contains(".csv")
								 && tempArgs[2].compareTo("-u") == 0 && isNumeric(tempArgs[3])
								 && tempArgs[4].compareTo("-p") == 0 && isNumeric(tempArgs[5])) {
			//
			jcv.analyseArgs(args);
			// 複製一份附本跟temp
			copyfile(file_Import_Name, file_Import_Name + CLONE);
			//
			createNewFile(file_Export_TabName);
			// 開始搜尋
			while (jcv.finish == false) {
				jcv.scanner(jcv.stringList);
			}
			// 刪除附本
			deleteFile(file_Import_Name + CLONE);
			// 是否要呼叫產生out檔
			if (exportOut) {
				jcv.linuxCommand(file_Export_TabName, file_Export_OutName);
			}
		}else if(tempArgs.length >= 2 && tempArgs[0].compareTo("-f") == 0 && tempArgs[1].contains(".csv") && new File(args[1]).exists() == false){
			System.out.print("Error: invalid input file.\r\n");		
		}else{
			System.out.print("jcvfpdata usage: java jcvfpdata -f [filename] -u N -p N -c *.tab -s N -o *.out\r\n");
			System.out.print("\t -c: Rename the tab file \r\n");
			System.out.print("\t -o: To export the *.out \r\n");
			System.out.print("\t -n: the number of relative order quantity.\r\n");
		}
		
	}
	
	private void analyseArgs(String[] args){
		file_Import_Name = args[1];
		file_Export_TabName = file_Import_Name.replace(".csv", ".tab");
		file_Export_OutName = file_Import_Name.replace(".csv", ".out");
		if (args.length > 2) {
			for (int i = 2; i < args.length; i++) {
				if (args[i].compareTo("-c") == 0 ) {
					if (args.length > i+1 && args[i+1].contains(".tab")) {
						file_Export_TabName = args[i+1];
					}else{
						file_Export_TabName = file_Import_Name.replace(".csv", ".tab");
					}
				}
				if (args[i].compareTo("-o") == 0 ) {
					exportOut = true;
					if (args.length > i+1 && args[i+1].contains(".out")) {
						file_Export_OutName = args[i+1];
					}else{
						file_Export_OutName = file_Import_Name.replace(".csv", ".out");
					}
				}
				if (args[i].compareTo("-s") == 0 ) {
					exportOut = true;
					if (args.length > i+1 && isNumeric(args[i+1])) {
						memberCount = Integer.valueOf(args[i+1]);
					}else{
						memberCount = 0;
					}
				}
				if (args[i].compareTo("-u") == 0 ) {
					ARRAY_USERID = Integer.valueOf(args[i+1]) - 1;
				}
				if (args[i].compareTo("-p") == 0 ) {
					ARRAY_BOOKID = Integer.valueOf(args[i+1]) - 1;
				}
			}
		}
	}
	
	private void scanner(List<List<String>> list){
		StringBuffer 	clone_StringBuffer = new StringBuffer();
		String 			userId = "";
		int 			num = 0;
		int				count = 0;
		stringList = list;
		ArrayList<String> bookidList = new ArrayList<String>();
		
		try {
			
			if (stringList == null) {
				System.out.println("initial..." );
				//
				Path filePath = new File(file_Import_Name + CLONE).toPath();
				Charset charset = Charset.defaultCharset();        
//				List<String> allLines = Files.readAllLines(filePath, charset);
//				List<String> allLines = readByStream(filePath);
//				List<String> allLines = readByBufferedStream(filePath, charset);
				List<String> allLines = loadByMappedByteBuffer(filePath.toFile());
				stringList = new ArrayList<List<String>>();
				while (allLines.size() > 0) {
					String temp 		= allLines.get(0);
					String[]	array	= temp.split(";");
					List<String> subList = new ArrayList<String>();
					subList.add(array[ARRAY_USERID].replaceAll("\"", ""));
					StringBuffer buffer = new StringBuffer(array[ARRAY_BOOKID].replaceAll("\"", ""));
					buffer.delete(0, 4);
					subList.add(buffer.toString());
					// 如果bookid不是全數字就掉
					if (isNumeric(subList.get(SUBLIST_BOOKID))) {
						stringList.add(subList);
					}
					allLines.remove(0);
				}
				num = stringList.size();
			}else{
				
				for (int i = 0; i < stringList.size(); i++) {
			   	 	if (stringList.get(i).size() > 1 ) {
			   	 		String tempUserId 	= stringList.get(i).get(SUBLIST_USERID);
			   	 		String bookId 		= stringList.get(i).get(SUBLIST_BOOKID);
			   	 		// 第一行userid當搜尋標準
			   	 		if ( num == 0) {
			   	 			userId = stringList.get(i).get(SUBLIST_USERID);
			   	 			bookidList.add(bookId);
			   	 			clone_StringBuffer.append(bookId);
			   	 			clone_StringBuffer.append(" ");
			   	 			count ++;
			   	 			stringList.remove(i);
			   	 			i--;
			   	 		}
			   	 		// 相同userid時把bookId抽出
			   	 		else if (tempUserId.compareTo(userId) == 0) {
			   	 			// 同樣bookId排除
			   	 			for (int j = 0; j < bookidList.size(); j++) {
								if (bookId.compareTo(bookidList.get(j)) != 0 && j == (bookidList.size()-1) ) {
									bookidList.add(bookId);
									clone_StringBuffer.append(bookId);
					   	 			clone_StringBuffer.append(" ");
					   	 			count ++;
					   	 			break;
								}else if (bookId.compareTo(bookidList.get(j)) == 0) {
									break;
								}
							}
			   	 			stringList.remove(i);
			   	 			i--;
						}
					}
			   	 	num++;
				}
			}
			
	        // 紀錄總筆數
	        if (first == true) {
	        	allcount = num;
	        	System.out.println(allcount +"/0" );
	        	first = false;
			}else{
				System.out.println(allcount +"/" +(allcount - num)  );
			}
	        // 搜尋結果寫入tab檔
	        if (clone_StringBuffer.length() > 1 && count >= memberCount) {
	        	clone_StringBuffer.delete((clone_StringBuffer.length()-1), clone_StringBuffer.length());
	        	writeText_UTF8_Apend(clone_StringBuffer.toString(), file_Export_TabName);
			}
	        // 釋放記憶體
	        clone_StringBuffer.setLength(1);
	        //
	        if (num == 0) {
	        	System.out.println("Finish");
	        	finish = true;
			}
	        
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IndexOutOfBoundsException e) {
			System.out.print("Parameter Invalid(-u N or -p N )");
		}catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	public static boolean isNumeric(String str)
	{
		Pattern pattern = Pattern.compile("[0-9]*");
		Matcher isNum = pattern.matcher(str);
		if( !isNum.matches() )
		{
			return false;
		}
		return true;
	}
	
	private void linuxCommand(String tab, String out){
		try {
			deleteFile(out);
			Process pl = Runtime.getRuntime().exec("./fpgrowth_linux "+ tab+ " "+ out);
	        String line = "";
	        BufferedReader p_in = new BufferedReader(new InputStreamReader(pl.getInputStream()));
	        while((line = p_in.readLine()) != null){
	                System.out.println(line);
	        }
	        p_in.close();
		} catch (Exception e) {
			// TODO: handle exception
		}
		
	}
	
	private static void copyfile(String srFile, String dtFile){
		try{
			File f1 = new File(srFile);
			File f2 = new File(dtFile);
			InputStream in = new FileInputStream(f1);
		  
			//For Append the file.
		//	      OutputStream out = new FileOutputStream(f2,true);
		
			//For Overwrite the file.
			OutputStream out = new FileOutputStream(f2);
			
			byte[] buf = new byte[1024];
			int len;
			while ((len = in.read(buf)) > 0){
				out.write(buf, 0, len);
			}
			in.close();
			out.close();
	    }
	    catch(FileNotFoundException ex){
	    	System.out.println(ex.getMessage() + " in the specified directory.");
	    	System.exit(0);
	    }
	    catch(IOException e){
	    	System.out.println(e.getMessage());      
	    }
	  }
	/**
     * 建立新檔(檔案已存在會刪除舊檔並建新檔)
     * @param path
     */
    public static void createNewFile(String path){
        try{
            File file = new File(path);
            deleteFile(path);
            file.createNewFile();
        }catch(IOException e){
            e.printStackTrace();
        }
    }
    
    public static void fileReName(String path, String newName){
    	File file = new File(path);
    	file.renameTo(new File(newName));
    }
    
    /**
     * 刪檔案
     * @param path
     */
    public static void deleteFile(String path){
        File file = new File(path);
        file.delete();
    }
    
    /**
     * 寫入文字檔(使用FileWriter 寫檔編碼為預設的iso-8859-1)，
     * 因此此method使用OutputStreamWriter寫檔，可自行指定格式
     *
     * @param text        將整個String寫入指定的檔案
     * @param filename    可用相對路徑或絕對路徑
     * @param format    寫入檔案的編碼格式
     * @param append    true 將此次寫檔串在原本檔案最後面 | false 將此次寫檔蓋掉原本的文字檔內容
     * @return            true 寫檔成功 | false 寫檔失敗
     */
    public boolean writeText(String text,String filename,String format,boolean append){
        if(text.equals("")){
            return false;
        }
        File file = new File(filename);//建立檔案，準備寫檔
        try{
            BufferedWriter bufWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file,append),format));
            bufWriter.write(text+"\n");
            bufWriter.close();
        }catch(IOException e){
            e.printStackTrace();
            System.out.println(filename + "寫檔發生錯誤");
            return false;
        }
        return true;
    }
	
    /**
     * 寫入檔案使用utf8格式寫檔，串在原本檔案內容後面
     * @param text
     * @param filename
     * @return
     */
    public boolean writeText_UTF8_Apend(String text,String filename){
        return writeText(text, filename, "utf8", true);
    }
    // =======================================================================
    public static boolean writeText_UTF8_Apend(List<String> list,String filename){
        return writeText(list, filename, "utf8", true);
    }
    
    public static boolean writeText(List<String> list,String filename,String format,boolean append){
        if(list.size() == 0){
            return false;
        }
        File file = new File(filename);//建立檔案，準備寫檔
        try{
            BufferedWriter bufWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file,append),format));
            for (int i = 0; i < list.size(); i++) {
            	bufWriter.write(list.get(i)+"\n");
			}
            bufWriter.close();
        }catch(IOException e){
            e.printStackTrace();
            System.out.println(filename + "寫檔發生錯誤");
            return false;
        }
        return true;
    }
    
    /** 
     * Reading a File by Using Stream I/O 
     *  
     * @param path 
     * @return 
     * @throws IOException 
     */  
    public static List<String> readByStream(Path path) throws IOException {  
        List<String> lines = new ArrayList<String>();  
        InputStream in = null;  
        BufferedReader reader = null;  
        try {  
            in = Files.newInputStream(path, StandardOpenOption.READ);  
            reader = new BufferedReader(new InputStreamReader(in));  
            String line = null;  
            while ((line = reader.readLine()) != null) {  
                lines.add(line);  
            }  
        } catch (IOException e) {  
            throw (e);  
        } finally {  
            if (in != null) {  
                in.close();  
            }  
            if (reader != null) {  
                reader.close();  
            }  
        }  
        return lines;  
    }  
    
    /** 
     * Reading a File by Using Buffered Stream I/O 
     *  
     * @param path 
     * @param cs 
     * @return 
     * @throws IOException 
     */  
    public static List<String> readByBufferedStream(Path path, Charset cs) throws IOException {  
        BufferedReader reader = null;  
        try {  
            reader = Files.newBufferedReader(path, cs);  
            String line = null;  
            List<String> lines = new ArrayList<String>();  
            while ((line = reader.readLine()) != null) {  
                lines.add(line);  
            }  
            return lines;  
        } catch (IOException e) {  
            throw (e);  
        } finally {  
            if (reader != null)  
                reader.close();  
        }  
    } 
    
    public static List<String> loadByMappedByteBuffer(File f) throws IOException {
        FileInputStream fis = new FileInputStream(f);
        FileChannel fc = fis.getChannel();
        List<String> lines = new ArrayList<String>();  
        
        MappedByteBuffer mmb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
        
        byte[] buffer = new byte[(int)fc.size()];
        mmb.get(buffer);
        
        fis.close();

        BufferedReader in = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buffer)));

        for (String line = in.readLine(); line != null; line = in.readLine()) {
        	lines.add(line);
        }

        in.close();
        return lines;
      }
}
